# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, fields, models, _
from odoo.addons import decimal_precision as dp
from odoo.exceptions import UserError
from odoo.tools import float_utils
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
from odoo.exceptions import ValidationError
from datetime import datetime
import calendar as cal


class inoutLine(models.Model):
    _name = "stock.inout.line"
    _description = u"进出汇总"
    _order = "location_id,product_id,start_date, prod_lot_id, package_id"

    month_balance_id = fields.Many2one("stock.month.balance", string=u"结存表")
    partner_id = fields.Many2one('res.partner', u'业务伙伴')
    up_inout_line_id = fields.Many2one('stock.inout.line', u'上个月明细')
    down_inout_line_id = fields.Many2one('stock.inout.line', u'下个月明细')
    abandon = fields.Boolean(u'已存在下个月', default=False)
    start_date = fields.Date(u'区间开始', copy=False)
    end_date = fields.Date(u'区间结束', copy=True)
    month = fields.Many2one("resource.calendar", string=u"月份", compute="_compute_month", store=True)
    product_id = fields.Many2one('product.product', u'产品', index=True, required=True)
    product_tmpl_id = fields.Many2one('product.template', u'产品', readonly=True, related='product_id.product_tmpl_id')
    product_uom_id = fields.Many2one('product.uom', u'单位')
    product_qty_in = fields.Float(u'入数量', digits=dp.get_precision('Product Unit of Measure'), default=0)
    product_qty_out = fields.Float(u'出数量', digits=dp.get_precision('Product Unit of Measure'), default=0)
    location_id = fields.Many2one('stock.location', u'库位', index=True, required=True)
    package_id = fields.Many2one('stock.quant.package', u'包裹', index=True)
    prod_lot_id = fields.Many2one('stock.production.lot', u'批次/序列号', domain="[('product_id','=',product_id)]")
    company_id = fields.Many2one('res.company', u'公司', index=True, readonly=True)

    _sql_constraints = [
        ("lot_location_uniq", "unique (start_date, end_date, location_id, prod_lot_id)", u"当月批次数据重复！")
    ]

    @api.depends("start_date")
    def _compute_month(self):
        self.update_month()

    def update_month(self):
        for line in self:
            if line.start_date:
                dt_start_date = fields.Date.from_string(line.start_date)
                line.month = self.env["resource.calendar"].search(
                    [('name', '=', "%d年%d月" % (dt_start_date.year, dt_start_date.month))],
                    limit=1)
            else:
                line.month = None

    # 先找当月的明细，如果找到，就更新进出数量；如果没找到，就新建一条明细
    @api.model
    def inout_update_create_report(self, product_id, location_id, qty, str_in_date, company_id, lot_id, package_id):
        # lot_id,package_id是id
        if location_id.usage != 'internal':
            return
        datetime_in_date = datetime.strptime(str_in_date, DEFAULT_SERVER_DATETIME_FORMAT)
        # datetime_for_date = datetime.strptime(str_in_date, DEFAULT_SERVER_DATE_FORMAT)
        if datetime_in_date > fields.datetime.now():
            raise ValidationError(u'对不起，强制日期不得为未来时间！')
        calendar_line = self.env['resource.calendar.attendance'].search([('date_from', '=', str_in_date.split()[0])])
        if calendar_line and calendar_line.calendar_id.inout_close:
            raise ValidationError(u'对不起，此期间已关帐！')
        inout_line = self.search([
            ('location_id', '=', location_id.id),
            ('prod_lot_id', '=', lot_id),
            ('start_date', '<=', str_in_date),
            ('end_date', '>=', str_in_date)
        ])
        if len(inout_line) > 1:
            raise ValidationError(u'警告！月报表记录中有多项该产品信息，请先联系系统管理员！')
        # 如果找到当月明细，则进行更新，如果没找到，就新建月明细
        if inout_line:
            # qty得到是，如果是入库位，正数，如果是出库位，负数，在报表中都要显示正数
            if qty > 0:
                inout_line.product_qty_in += qty
            elif qty < 0:
                inout_line.product_qty_out += -qty
                # inout_line.product_qty_end = inout_line.product_qty_start + inout_line.product_qty_in - inout_line.product_qty_out
        else:
            abandon_inout_lines = self.search(
                [('product_id', '=', product_id.id), ('location_id', '=', location_id.id),
                 ('company_id', '=', company_id.id),
                 ('prod_lot_id', '=', lot_id),
                 ('package_id', '=', package_id)])
            for abandon_inout_line in abandon_inout_lines:
                abandon_inout_line.abandon = True
            inout_line = self.create(
                self.get_valus(product_id, location_id, qty, datetime_in_date, company_id, lot_id, package_id))

    @api.model
    def get_valus(self, product_id, location_id, qty, datetime_in_date, company_id, lot_id=None, package_id=None):
        up_inout_line_id = self._get_up_inout_line(location_id, product_id, package_id,
                                                   str_date=datetime_in_date, company_id=company_id,
                                                   prod_lot_id=lot_id)
        # qty得到是，如果是入库位，正数，如果是出库位，负数，在报表中都要显示正数
        if qty < 0:
            product_qty_in = 0
            product_qty_out = -qty
        else:
            product_qty_in = qty
            product_qty_out = 0
        # product_qty_end = up_inout_line_id.product_qty_end + product_qty_in - product_qty_out

        start_date, end_date = self.env['resource.calendar']._get_next_num_month(datetime_in_date, 0)
        return {
            'abandon': False,
            'up_inout_line_id': up_inout_line_id.id,
            'product_id': product_id.id,
            'product_uom_id': product_id.uom_id.id,
            'location_id': location_id.id,
            'product_qty_in': product_qty_in,
            'product_qty_out': product_qty_out,
            # 'product_qty_end': product_qty_end,
            'start_date': start_date,
            'end_date': end_date,
            'company_id': company_id.id,
            'prod_lot_id': lot_id,
            'package_id': package_id,
        }

    @api.model
    def _get_up_inout_line(self, location_id, product_id, package_id, str_date, company_id, prod_lot_id):
        if not product_id:
            return 0
        # 因为有强制日期，所以不能通过库存数量来作为期初数据。而是用找到的上个月明细期末数，如果没找到，就是0
        last_start_date, last_end_date = self.env['resource.calendar']._get_next_num_month(str_date, -1)
        inout_line = self.search(
            [('product_id', '=', product_id.id), ('location_id', '=', location_id.id),
             ('company_id', '=', company_id.id),
             ('prod_lot_id', '=', prod_lot_id),
             ('package_id', '=', package_id),
             ('start_date', '=', last_start_date),
             ('end_date', '=', last_end_date)])
        return inout_line

    @api.onchange('product_id')
    def onchange_product(self):
        res = {}
        if self.product_id:
            self.product_uom_id = self.product_id.uom_id
            res['domain'] = {'product_uom_id': [('category_id', '=', self.product_id.uom_id.category_id.id)]}
        return res

    @api.model
    def _get_quants(self, location_id, product_id, package_id, company_id, prod_lot_id):
        return self.env['stock.quant'].search([
            ('company_id', '=', company_id.id),
            ('location_id', '=', location_id.id),
            ('lot_id', '=', prod_lot_id),
            ('product_id', '=', product_id.id),
            ('package_id', '=', package_id)])


class StockMonthBalance(models.Model):
    _name = "stock.month.balance"
    _description = u"结存表"

    month = fields.Many2one("resource.calendar", string=u"月份", required=True, index=True, readonly=True)
    inout_close = fields.Boolean(related="month.inout_close")
    product_id = fields.Many2one('product.product', u'产品', store=True, readonly=True)
    product_tmpl_id = fields.Many2one('product.template', u'产品', readonly=True,
                                      related='product_id.product_tmpl_id')
    product_uom_id = fields.Many2one('product.uom', u'单位', readonly=True)
    product_qty_start = fields.Float(string=u"期初数量", readonly=True)
    product_qty_end = fields.Float(u'期末结存', default=0, readonly=True)
    product_qty_in = fields.Float(u'本期入库数量', default=0)
    product_qty_out = fields.Float(u'本期出库数量', default=0)
    prod_lot_id = fields.Many2one('stock.production.lot', u'批次/序列号', readonly=True)
    location_id = fields.Many2one('stock.location', u'库位', readonly=True)

    @api.multi
    def name_get(self):
        return [
            (record.id, "%s - %s" % (record.product_id and record.product_id.name_get()[0][1] or "", record.month.name))
            for record in self]

    def inout_carry_settle(self, calendar):
        self.env.cr.execute("""delete from stock_month_balance where month = %s""", (calendar.id,))
        domain = [
            ("month", "=", calendar.id),
            ("location_id.usage", "=", "internal"),
        ]
        report_lines = self.env["stock.month.balance.report"].search(domain)
        if report_lines:
            sql = """insert into stock_month_balance (prod_lot_id,product_id,product_uom_id, location_id,month,product_qty_in,product_qty_out,product_qty_start,product_qty_end) values """
            sql += ",".join("(%s,%s,%s,%s,%d,%f,%f,%f,%f)" % (
                (line.prod_lot_id and line.prod_lot_id.id > 0) and str(line.prod_lot_id.id) or "NULL",
                line.product_id and str(line.product_id.id) or "NULL",
                line.product_uom_id and str(line.product_uom_id.id) or "NULL",
                line.location_id.id,
                calendar.id,
                line.product_qty_in,
                line.product_qty_out,
                line.product_qty_start,
                line.product_qty_end
            ) for line in report_lines)
            self.env.cr.execute(sql)

    def update_product_qty_end(self):
        sql = """update stock_month_balance 
                 set product_qty_end = product_qty_start + product_qty_in - product_qty_out
                 where id in %s and previous_month_balance_id is not null """
        self.env.cr.execute(sql, (tuple(self.ids),))
        sql = """update stock_month_balance 
                 set product_qty_end = product_qty_in - product_qty_out
                 where id in %s and previous_month_balance_id is null """
        self.env.cr.execute(sql, (tuple(self.ids),))
